package uk.ac.sanger.das.dasregistry.springrest.coordinates;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;


import org.biodas.jdas.schema.registrycoordinates.DASCOORDINATESYSTEM;
import org.biodas.jdas.schema.sources.*;
import org.biodas.jdas.dassources.utils.DasSourceComparator;
import org.biodas.jdas.dassources.utils.DasSpec;
import org.biodas.jdas.dassources.utils.SourcesFilter;
import org.biodas.jdas.servlet.utils.DASQueryStringTranslator;
import org.biodas.jdas.servlet.utils.RowsFilter;


import uk.ac.sanger.das.dasregistry.springrest.security.Authenticate;
import uk.ac.sanger.das.dasregistry.springrest.sources.DasSourceNotRegisterableExcepton;
import uk.ac.sanger.das.dasregistry.springrest.sources.SourcesController;
import uk.ac.sanger.solr.GetResultsFromSanger;

import org.apache.log4j.BasicConfigurator;

@Controller
public class CoordinatesController {


		static Logger logger = Logger.getLogger(CoordinatesController.class);
		private CoordinatesManager coordinatesManager;
		private SOURCES storedSources;
		private String regCoordPrefix="http://www.dasregistry.org/dasregistry/coordsys/CS_DS";
		Authenticate authenticate;

		/**
		 * Creates a new DasSourceController with a given DasSource manager.
		 */
		@Autowired
		public CoordinatesController(CoordinatesManager coordinatesManager, Authenticate authenticate) {
			this.coordinatesManager = coordinatesManager;
			this.authenticate=authenticate;
		}

		
		@RequestMapping(value = "/coordinatesystem*", method = { RequestMethod.GET,
				RequestMethod.HEAD })
		public @ResponseBody
		DASCOORDINATESYSTEM listCoordinates(HttpServletRequest req, HttpServletResponse resp) {

			// We need this here as tomcat doesn't seperate params by ; by default
			// so we have to alter the request
			req = new DASQueryStringTranslator(req);
			setHeaders(resp);
			logger.info("get request for coordinates " + req.getQueryString());

			List<String> coordinateId = getParameterValuesAsListFor(req,
					"coordinateid");
			List<String> organism = getParameterValuesAsListFor(req, "organism");
			List<String> authority = getParameterValuesAsListFor(req, "authority");
			List<String> csName = getParameterValuesAsListFor(req, "CSName");
			List<String> version = getParameterValuesAsListFor(req, "version");// version
																			
			List<String> type = getParameterValuesAsListFor(req, "type");// type
																			// in
																			// coordinate
			// system e.g.
			// chromosome
			// String csType[] = req.getParameterValues("CSTypes");
			// if (csType != null && csType != "") {
			// type = csType;
			// }

			String rows = req.getParameter("rows");
			// coordinate
			// system
			String keywords = req.getParameter("keywords");
		
			
		
			DASCOORDINATESYSTEM coordinates = coordinatesManager.getAllCoordinates();
			// cache all the sources and we can filter them if necessary

			// sources to write is local copy per servlet request so if order
			// changes we aren't changing the order for all susequent reqeusts
			List<COORDINATES> coordsToWrite = new ArrayList<COORDINATES>();
			// filter by the searh if keywords have been specified using sanger
			// search results
			if (keywords != null && keywords != "") {
				GetResultsFromSanger sanger = new GetResultsFromSanger();
				List<String> ids = sanger.getCoordinatesForKeyWords(keywords);
				logger.debug(ids.toString());
				for (COORDINATES coord : coordinates.getCOORDINATES()) {
					String coordId = coord.getUri();
					if(coordId.contains(DasCoordintesDaoSangerRegistryJDAS.COORDINATE_ROOT_URI)){
						coordId=coordId.replace(DasCoordintesDaoSangerRegistryJDAS.COORDINATE_ROOT_URI, "");
					}
					logger.debug("coordId="+coord.getUri());
					// System.out.println(sourceId);
					if (ids.contains(coordId)) {
						coordsToWrite.add(coord);
					}
				}
				
			}else{
				coordsToWrite=coordinates.getCOORDINATES();
			}
			DASCOORDINATESYSTEM coordSystem=new DASCOORDINATESYSTEM();
			
			if(rows!=null){
				logger.debug("filtering rows");
				RowsFilter rowsF = new RowsFilter();
				coordsToWrite = rowsF.filterByRows(coordsToWrite, req);
				coordSystem.setTotal(rowsF.getTotal());
				coordSystem.setStart(rowsF.getStart());
				coordSystem.setEnd(rowsF.getStop());
			}
			
			coordSystem.getCOORDINATES().addAll(coordsToWrite);
			return coordSystem; 
			
		
		}


		private void setHeaders(HttpServletResponse resp) {
			SourcesController.setHeaders(resp);
		}


		@RequestMapping(value = "coordinatesystem/CS_DS{id}", method = RequestMethod.GET)
		public @ResponseBody
		DASCOORDINATESYSTEM singleCoordinate(@PathVariable("id") String id, HttpServletResponse resp) {
			setHeaders(resp);
			return getSingleCoordinate(id);
		}
		
		@RequestMapping(value = "coordsys/CS_DS{id}", method = RequestMethod.GET)
		public @ResponseBody
		DASCOORDINATESYSTEM singleCoordinateOldStyle(@PathVariable("id") String id, HttpServletResponse resp) {
			setHeaders(resp);
			return getSingleCoordinate(id);
		}

		private DASCOORDINATESYSTEM getSingleCoordinate(String id) {
			logger.debug("request for single source " + id);			
			DASCOORDINATESYSTEM coordList = coordinatesManager.getAllCoordinates();
			DASCOORDINATESYSTEM newCoords = new DASCOORDINATESYSTEM();
			for (COORDINATES coord : coordList.getCOORDINATES()) {
				logger.debug("id="+id+" coord.getUri="+coord.getUri());
				if (coord.getUri().equals( regCoordPrefix+id)) {

					newCoords.getCOORDINATES().add(coord);
					return newCoords;
				}
			}
			return newCoords;
		}

		@RequestMapping(value = "/coordinatesystem*", method = { RequestMethod.POST })
		public @ResponseBody
		DASCOORDINATESYSTEM add(@RequestBody DASCOORDINATESYSTEM coordinate, HttpServletRequest req, HttpServletResponse resp) {
			setHeaders(resp);
			DASCOORDINATESYSTEM createdCoords = null;
			if (authenticated(req)) {
			try {
				createdCoords = coordinatesManager.add(coordinate);
				resp.setStatus(HttpStatus.CREATED.value());
			} catch (DasCoordinateNotRegisterableExcepton e) {
				//  Auto-generated catch block
				e.printStackTrace();
				// need to set the response status here otherwise no client side
				// error will be thrown!
				resp.setStatus(HttpStatus.NOT_ACCEPTABLE.value());
				PrintWriter writer = null;
				try {
					writer = resp.getWriter();
				} catch (IOException e1) {
					//  Auto-generated catch block
					e1.printStackTrace();
				}
				writer.write("errror=" + e.getMessage());
				return null;
			}
			}else{
				resp.setStatus(HttpStatus.UNAUTHORIZED.value());
				
			}
			return createdCoords;			
		}

		public List<String> getParameterValuesAsListFor(HttpServletRequest req,
				String param) {
			List<String> list = new ArrayList<String>();
			String[] values = req.getParameterValues(param);
			if (values != null) {
				for (String value : values) {
					logger.debug("param value=" + value);
				}
				list = Arrays.asList(values);
			}
			return list;
		}
		
		private boolean authenticated(HttpServletRequest req) {
			if (authenticate == null) {
				// if no authentication then we assume the setup doesn't want any
				// and we just say yes to every request
				logger.warn("auth is null");
				return true;
			}
			String auth = req.getHeader("authorization");

			logger.debug("authorization" + auth);

			String isSecure = "false";// should set this by config so ignore on dev,
										// enforce on live. previously done through
										// servlet context
			if (isSecure.equals("true")) {
				// if not secure ie. on mac then just don't do any of this https
				// checking
				// if we are on live or sandbox isSecure should be true then lets
				// check the protocol is https
				// do this by checking the header X-is-ssl=1 which is set by the
				// front ends
				String sslHeader = req.getHeader("X-is-ssl") + "";
				if (!sslHeader.equals("1")) {
					System.out
							.println("Needs to be an https request for this request");
					return false;
				}

			}

			if (authenticate.authenticate(auth)) {
				logger.warn("allowed");
				return true;
			}
			// need to test user and password here using and authentication method -
			// interface here?
			return false;
		}
}
